#include <iostream>
#include <string>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SOCKET_ERROR -1
#endif
#include <event.h>
#include <event2/bufferevent_ssl.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>
#include <thread>
#include "XSSL/XSSLCtx.h"
#include <string.h>
// extern "C"{
//     #include <openssl/applink.c>
// };
using namespace std;
void ReadCB(struct bufferevent *bev, void *ctx){
    cout<<"read"<<endl;
    char buf[1024]={0};
    int len = bufferevent_read(bev,buf,sizeof(buf));
    if(len>0){
        cout<<"read:"<<buf<<endl;
        const char *buf="client";
        bufferevent_write(bev,buf,strlen(buf));
    }
}
void WriteCB(struct bufferevent *bev, void *ctx){

}
void EventCB(struct bufferevent *bev, short what, void *ctx){
    cout<<"event"<<endl;
    if(what&BEV_EVENT_CONNECTED){// 密钥协商成功后静茹
        cout<<"connect success"<<endl;
        string data = "buffervent client send";
        int ret = bufferevent_write(bev, data.c_str(), data.size());
        auto ssl = (XSSL*)ctx;
        ssl->PrintCipher();
        ssl->PrintPeerCert();
    }else if(what&BEV_EVENT_ERROR){
        cerr<<"connect error"<<endl;
    }
}

void SReadCB(struct bufferevent *bev, void *ctx){
    cout<<"sread"<<endl;
    char buf[1024]={0};
    int len = bufferevent_read(bev,buf,sizeof(buf));
    if(len>0){
        cout<<"read:"<<buf<<endl;
        const char *buf="server";
        bufferevent_write(bev,buf,strlen(buf));
    }
}
void SWriteCB(struct bufferevent *bev, void *ctx){

}
void SEventCB(struct bufferevent *bev, short what, void *arg){
    cout<<"SEventCB"<<endl;
    if(what&BEV_EVENT_CONNECTED){// 密钥协商成功后静茹
        cout<<"connect success"<<endl;
        auto ssl = (XSSL*)arg;
        // ssl->PrintCipher();
        // ssl->PrintPeerCert();
    }else if(what&BEV_EVENT_ERROR){
        cerr<<"connect error"<<endl;
    }
}

void ListenerCB(struct evconnlistener *listener, evutil_socket_t sock, struct sockaddr *addr, int socklen, void *arg){
    XSSLCtx* ctx=(XSSLCtx*)arg;
    auto xssl  = ctx->NewXSSL(sock);
    auto *pxssl = new XSSL(xssl);
    auto base = evconnlistener_get_base(listener);
    auto bev = bufferevent_openssl_socket_new(
        base, sock,
        xssl.ssl(),
        BUFFEREVENT_SSL_ACCEPTING,// 对应的客户端
        BEV_OPT_CLOSE_ON_FREE // bufferevent_free()的时候关闭socket和释放关联的ssl
    );
    if (!bev)
    {
        cout<<"bufferevent_openssl_socket_new error"<<endl;
        return;
    }
    bufferevent_enable(bev, EV_READ | EV_WRITE);
    bufferevent_setcb(bev,
        SReadCB, SWriteCB, SEventCB, &pxssl);
    // event_add((event*)bev, 0);
}
int main(int argc,char* argv[]){
    // 测试openssl的tls加密解密
#ifdef _WIN32
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
    int port = 20300;
    if(argc==2){// 服务端
        port = atoi(argv[1]);
    }else if(argc>2){
        string ip = argv[2];
        auto base = event_base_new();
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        XSSLCtx ctx;
        ctx.InitClient("server.crt");
        XSSL ssl = ctx.NewXSSL(sock);
        auto bev = bufferevent_openssl_socket_new(
            base, sock,
            ssl.ssl(),
            BUFFEREVENT_SSL_CONNECTING,// 对应的客户端
            BEV_OPT_CLOSE_ON_FREE // bufferevent_free()的时候关闭socket和释放关联的ssl
        );
        if(!bev){
            cerr<<"bufferevent_openssl_socket_new error"<<endl;
            ctx.Close();
            getchar();
            return -1;
        }
        sockaddr_in addr={0};
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        bufferevent_setcb(bev,
            ReadCB, WriteCB, EventCB, &ssl);
        bufferevent_enable(bev, EV_READ | EV_WRITE);
        int ret = bufferevent_socket_connect(bev, (sockaddr *)&addr, sizeof(addr));
        if(ret == SOCKET_ERROR){
            cerr<<"connect error "<<ip<<":"<<port<<"failed!"<<endl;
            ctx.Close();
            getchar();
            return 1;
        }
        event_base_dispatch(base);
        ctx.Close();
        return 0;
    }
    auto base = event_base_new();
    XSSLCtx ctx;
    bool ret = ctx.InitServer("server.crt","server.key","server.crt");
    if(!ret){
        cerr<<"InitServer err"<<endl;
        return -1;
    }
    sockaddr_in addr={0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;
    evconnlistener_new_bind(
            base, 
            ListenerCB,
            &ctx,
            LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, 
            10,
            (sockaddr *)&addr,
            sizeof(addr)
        );
    event_base_dispatch(base);
}